package com.htn.auth.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONObject;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import com.htn.auth.form.AccountTypeEnum;
import com.htn.auth.form.LoginRequestVO;
import com.htn.auth.form.OneClickLoginType;
import com.htn.common.core.base.DataResponse;
import com.htn.common.core.constant.*;
import com.htn.common.core.domain.WxLoginVO;
import com.htn.common.core.exception.CaptchaException;
import com.htn.common.core.util.CommonUtils;
import com.htn.common.core.util.ErrorCodeUtil;
import com.htn.common.core.utils.HttpClientUtil;
import com.htn.common.redis.service.RedisService;
import com.htn.common.security.service.TokenService;
import com.htn.system.api.model.DriverUserDTO;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.htn.common.core.domain.R;
import com.htn.common.core.enums.UserStatus;
import com.htn.common.core.exception.BaseException;
import com.htn.common.core.utils.SecurityUtils;
import com.htn.common.core.utils.ServletUtils;
import com.htn.common.core.utils.StringUtils;
import com.htn.common.core.utils.ip.IpUtils;
import com.htn.system.api.RemoteLogService;
import com.htn.system.api.RemoteUserService;
import com.htn.system.api.domain.SysLogininfor;
import com.htn.system.api.domain.SysUser;
import com.htn.system.api.model.LoginUser;
import org.springframework.util.MultiValueMap;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.InvalidParameterSpecException;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 登录校验方法
 * 
 * @author htn
 */
@Component
public class SysLoginService
{
    @Autowired
    private RemoteLogService remoteLogService;

    @Autowired
    private RemoteUserService remoteUserService;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private RedisService redisCache;



    /**
     * 登录
     */
    public LoginUser login(String username, String password){
        // 用户名或密码为空 错误
        if (StringUtils.isAnyBlank(username, password))
        {
            recordLogininfor(username, Constants.LOGIN_FAIL, "用户/密码必须填写");
            throw new BaseException("用户/密码必须填写");
        }
        // 密码如果不在指定范围内 错误
        if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
                || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
        {
            recordLogininfor(username, Constants.LOGIN_FAIL, "用户密码不在指定范围");
            throw new BaseException("用户密码不在指定范围");
        }
        // 用户名不在指定范围内 错误
        if (username.length() < UserConstants.USERNAME_MIN_LENGTH
                || username.length() > UserConstants.USERNAME_MAX_LENGTH)
        {
            recordLogininfor(username, Constants.LOGIN_FAIL, "用户名不在指定范围");
            throw new BaseException("用户名不在指定范围");
        }
        // 查询用户信息
        R<LoginUser> userResult = remoteUserService.getUserInfo(username, SecurityConstants.INNER);

        if (R.FAIL == userResult.getCode())
        {
            throw new BaseException(userResult.getMsg());
        }

        if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData()))
        {
            recordLogininfor(username, Constants.LOGIN_FAIL, "登录用户不存在");
            throw new BaseException("登录用户：" + username + " 不存在");
        }
        LoginUser userInfo = userResult.getData();
        SysUser user = userResult.getData().getSysUser();
        userInfo.setUserId(user.getUserId());
        userInfo.setName(user.getNickName());
        userInfo.setUsername(user.getUserName());

        if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
        {
            recordLogininfor(username, Constants.LOGIN_FAIL, "对不起，您的账号已被删除");
            throw new BaseException("对不起，您的账号：" + username + " 已被删除");
        }
        if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
        {
            recordLogininfor(username, Constants.LOGIN_FAIL, "用户已停用，请联系管理员");
            throw new BaseException("对不起，您的账号：" + username + " 已停用");
        }
        if (!SecurityUtils.matchesPassword(password, user.getPassword()))
        {
            recordLogininfor(username, Constants.LOGIN_FAIL, "用户密码错误");
            throw new BaseException("用户不存在/密码错误");
        }
        recordLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功");
        return userInfo;
    }

    public void logout(String loginName){
        recordLogininfor(loginName, Constants.LOGOUT, "退出成功");
    }

    /**
     * 注册
     */
    public void register(String username, String password){
        // 用户名或密码为空 错误
        if (StringUtils.isAnyBlank(username, password))
        {
            throw new BaseException("用户/密码必须填写");
        }
        if (username.length() < UserConstants.USERNAME_MIN_LENGTH
                || username.length() > UserConstants.USERNAME_MAX_LENGTH)
        {
            throw new BaseException("账户长度必须在2到20个字符之间");
        }
        if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
                || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
        {
            throw new BaseException("密码长度必须在5到20个字符之间");
        }

        // 注册用户信息
        SysUser sysUser = new SysUser();
        sysUser.setUserName(username);
        sysUser.setNickName(username);
        sysUser.setPassword(SecurityUtils.encryptPassword(password));
        R<?> registerResult = remoteUserService.registerUserInfo(sysUser, SecurityConstants.INNER);

        if (R.FAIL == registerResult.getCode())
        {
            throw new BaseException(registerResult.getMsg());
        }
        recordLogininfor(username, Constants.REGISTER, "注册成功");
    }

    /**
     * 记录登录信息
     * 
     * @param username 用户名
     * @param status 状态
     * @param message 消息内容
     * @return
     */
    public void recordLogininfor(String username, String status, String message){
        SysLogininfor logininfor = new SysLogininfor();
        logininfor.setUserName(username);
        logininfor.setIpaddr(IpUtils.getIpAddr(ServletUtils.getRequest()));
        logininfor.setMsg(message);
        // 日志状态
        if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER))
        {
            logininfor.setStatus("0");
        }
        else if (Constants.LOGIN_FAIL.equals(status))
        {
            logininfor.setStatus("1");
        }
        remoteLogService.saveLogininfor(logininfor, SecurityConstants.INNER);
    }

        public DataResponse commonLogin(LoginRequestVO loginRequest) {

        String type = loginRequest.getType();
        String username = loginRequest.getUsername();
        String code = loginRequest.getCode();
        if (!type.equals(CommonConstants.UserRole.BIZ.name()) && !OneClickLoginType.ONE.getValue().equals(loginRequest.getFlag())) {
            DataResponse res = remoteUserService.checkSmsCode(username,type,code,SecurityConstants.INNER);
            Object re = res.getData();
            if(StringUtils.isNull(re) || (StringUtils.isNotNull(re) && CommonConstants.NO.equals(re.toString())) ){
                return DataResponse.failure(CommonConstants.ResponseStatus.CODE_ERROR);
            }
        }

        String ifExistsItem="0";
        HashMap<String, String> dataMap = Maps.newHashMap();

        LoginUser loginUser = new LoginUser();
        if(AccountTypeEnum.BIZ.getValue().equals(type)){
            R<DriverUserDTO> date = remoteUserService.findByUsername(username,type,SecurityConstants.INNER);
            if(date.getCode() == R.FAIL){
                return new DataResponse(CommonConstants.ResponseStatus.FAIL,date.getMsg());
            }
            DriverUserDTO hsmwBizUser = date.getData();
            loginUser.setUserId(hsmwBizUser.getId());
            loginUser.setType(loginRequest.getType());
            loginUser.setUsername(loginRequest.getUsername());
            loginUser.setName(hsmwBizUser.getName());
            loginUser.setEnabled(hsmwBizUser.getEnabled());
        }
        if(AccountTypeEnum.CONCRETE.getValue().equals(type)){
            R<DriverUserDTO> date = remoteUserService.findByUsername(username,type,SecurityConstants.INNER);
            if(date.getCode() == R.FAIL){
                return new DataResponse(CommonConstants.ResponseStatus.FAIL,date.getMsg());
            }
            DriverUserDTO hsmwConcreteUser = date.getData();
            loginUser.setUserId(hsmwConcreteUser.getId());
            loginUser.setType(loginRequest.getType());
            loginUser.setUsername(loginRequest.getUsername());
            loginUser.setName(hsmwConcreteUser.getName());
            loginUser.setEnabled(hsmwConcreteUser.getEnabled());
        }

        if(AccountTypeEnum.TPSP.getValue().equals(type)){
            R<DriverUserDTO> date = remoteUserService.findByUsername(username,type,SecurityConstants.INNER);
            if(date.getCode() == R.FAIL){
                return new DataResponse(CommonConstants.ResponseStatus.FAIL,date.getMsg());
            }
            DriverUserDTO hsmwTpspUser = date.getData();
            loginUser.setUserId(hsmwTpspUser.getId());
            loginUser.setType(loginRequest.getType());
            loginUser.setUsername(loginRequest.getUsername());
            loginUser.setName(hsmwTpspUser.getName());
            loginUser.setEnabled(hsmwTpspUser.getEnabled());
        }
        if(AccountTypeEnum.SALE.getValue().equals(type)){
            R<DriverUserDTO> date = remoteUserService.findByUsername(username,type,SecurityConstants.INNER);
            if(date.getCode() == R.FAIL){
                return new DataResponse(CommonConstants.ResponseStatus.FAIL,date.getMsg());
            }
            DriverUserDTO hsmwSaleUser = date.getData();
            loginUser.setUserId(hsmwSaleUser.getId());
            loginUser.setType(loginRequest.getType());
            loginUser.setUsername(loginRequest.getUsername());
            loginUser.setName(hsmwSaleUser.getName());
            loginUser.setEnabled(hsmwSaleUser.getEnabled());
        }
        if(AccountTypeEnum.PM.getValue().equals(type)){
            R<DriverUserDTO> date = remoteUserService.findByUsername(username,type,SecurityConstants.INNER);
            if(date.getCode() == R.FAIL){
                return new DataResponse(CommonConstants.ResponseStatus.FAIL,date.getMsg());
            }
            DriverUserDTO hsmwPmUser = date.getData();
            loginUser.setUserId(hsmwPmUser.getId());
            loginUser.setType(loginRequest.getType());
            loginUser.setUsername(loginRequest.getUsername());
            loginUser.setName(hsmwPmUser.getName());
            loginUser.setEnabled(hsmwPmUser.getEnabled());

           /* List<HsmwItem> items = hsmwItemService.list(new LambdaQueryWrapper<HsmwItem>()
                    .eq(HsmwItem::getCreateUser,hsmwPmUser.getId()));
            if(CollectionUtil.isNotEmpty(items)){
                ifExistsItem="1";
            }*/
            dataMap.put("ifExistsItem",ifExistsItem);
            dataMap.put("auditStatus",hsmwPmUser.getAuditStatus());
        }
        if(AccountTypeEnum.DRIVER.getValue().equals(type)){
            R<DriverUserDTO> date = remoteUserService.findByUsername(username,type,SecurityConstants.INNER);
            if(date.getCode() == R.FAIL){
                return new DataResponse(CommonConstants.ResponseStatus.FAIL,date.getMsg());
            }
            DriverUserDTO hsmwDriverUser = date.getData();
            loginUser.setUserId(hsmwDriverUser.getId());
            loginUser.setType(loginRequest.getType());
            loginUser.setUsername(loginRequest.getUsername());
            loginUser.setName(hsmwDriverUser.getName());
            loginUser.setEnabled(hsmwDriverUser.getEnabled());
        }
        if(AccountTypeEnum.HTN.getValue().equals(type) || "SELFRUN".equals(type) || "FOREMAN".equals(type)){
            R<DriverUserDTO> date = remoteUserService.findByUsername(username,type,SecurityConstants.INNER);
            if(date.getCode() == R.FAIL){
                return new DataResponse(CommonConstants.ResponseStatus.FAIL,date.getMsg());
            }
            DriverUserDTO hsmwHtnUser = date.getData();
            loginUser.setUserId(hsmwHtnUser.getId());
            loginUser.setType(loginRequest.getType());
            loginUser.setUsername(loginRequest.getUsername());
            loginUser.setName(loginRequest.getUsername());
            loginUser.setEnabled(hsmwHtnUser.getEnabled());
        }
        Map<String, Object> token = tokenService.createToken(loginUser);

        recordLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功");

        return new DataResponse(CommonConstants.ResponseStatus.SUCCESS,token);
    }


    public DataResponse wxCode(WxLoginVO vo) {
        System.out.println("wxCode传的参数为 ==="+vo.toString());
        if(StringUtils.isBlank(vo.getCode())){
            return  DataResponse.failure(CommonConstants.ResponseStatus.VALIDATE_FAILED.getCode(),"您输入的code为空，请重新输入！ ");
        }
        Map  result= new HashMap();
        String  res ="";
        Map  param = new HashMap();
        if(AccountTypeEnum.CONCRETE.getValue().equals(vo.getType())){
            param.put("appid",CommonConstants.CONCRETE_APPID);
            param.put("secret",CommonConstants.CONCRETE_SECRET);
        }
        if(AccountTypeEnum.PM.getValue().equals(vo.getType())){
            param.put("appid",CommonConstants.WX_PM_APPID);
            param.put("secret",CommonConstants.WX_PM_APP_SECRET);
        }
        if(AccountTypeEnum.SALE.getValue().equals(vo.getType())){
            param.put("appid",CommonConstants.SALE_APPID);
            param.put("secret",CommonConstants.SALE_APP_SECRET);
        }
        if(AccountTypeEnum.TPSP.getValue().equals(vo.getType())){
            param.put("appid",CommonConstants.TPSP_APPID);
            param.put("secret",CommonConstants.TPSP_APP_SECRET);
        }
        param.put("js_code",vo.getCode());
        param.put("grant_type",CommonConstants.WX_GRANT_TYPE);
        try{
            res = HttpClientUtil.doGet(CommonConstants.WX_LOGIN_URL,param);
        }catch (Exception e){
            e.printStackTrace();
            return  DataResponse.failure(ErrorCodeUtil.E_500,res);
        }
        com.alibaba.fastjson.JSONObject jsonObject = com.alibaba.fastjson.JSONObject.parseObject(res);
        String  errorCode = jsonObject.getString("errcode");
        String errmsg = jsonObject.getString("errmsg");
        if(StringUtils.isNotBlank(errorCode)){
            return  DataResponse.failure(ErrorCodeUtil.E_220,"请求微信数据异常，"+errmsg);
        }
        String sessionKey = jsonObject.getString("session_key");
        String openId = jsonObject.getString("openid");
        JSONObject object = new JSONObject();
        object.put("sessionKey",sessionKey);
        object.put("openId",openId);
        if(StringUtils.isBlank(sessionKey)){
            return  DataResponse.failure(ErrorCodeUtil.E_220,"通过code获取的sessionKey为空，请重新输入！");
        }
        String  codeKey ="";
        if(StringUtils.isBlank(vo.getCodeKey())){
            codeKey = CommonUtils.generateCodeKey();
        }else{
            codeKey = vo.getCodeKey();
        }
        //获取sessionKey以后,存入到redis 中， key是 获取的或者是 传过来的 。
        redisCache.setCacheObject(codeKey, object, CommonConstants.WXCODE_EXPIRATION, TimeUnit.MINUTES);
        Map ret = new HashMap();
        ret.put("codeKey",codeKey);
        System.out.println("返回的codekey为== "+ codeKey);

        return  new DataResponse(ret);
    }


    public DataResponse wxLogin(WxLoginVO vo) {

          /*  属性	类型	默认值	必填	说明
        appid	string		是	小程序 appIdi
        secret	string		是	小程序 appSecret
        js_code	string		是	登录时获取的 code
        grant_type	string		是	授权类型，此处只需填写 authorization_code
        */
        System.out.println(vo.toString());
        if(StringUtils.isBlank(vo.getEncryptedData())){
            return  DataResponse.failure(ErrorCodeUtil.E_220,"您输入的待解码数据为空，请重新输入！");
        }
        if(StringUtils.isBlank(vo.getOffset())){
            return  DataResponse.failure(ErrorCodeUtil.E_220,"您输入的偏移量数据为空，请重新输入！");
        }
        if(StringUtils.isBlank(vo.getCodeKey())){
            return  DataResponse.failure(ErrorCodeUtil.E_220,"您输入的codeKey为空，请重新输入！");
        }
        //请求返回值
        String sessionParam = redisCache.getCacheObject(vo.getCodeKey());
        com.alibaba.fastjson.JSONObject sessionObject = JSON.parseObject(sessionParam);
        String sessionKey = sessionObject.getString("sessionKey");
        String openId = sessionObject.getString("openId");


        if(StringUtils.isBlank(sessionKey)){
            return  DataResponse.failure(ErrorCodeUtil.E_407,"sessionKey为空，请重新进入小程序！");
        }
        System.out.println("解密出来的sessionKey == "+ sessionKey);
        //存表操作
        com.alibaba.fastjson.JSONObject object= new com.alibaba.fastjson.JSONObject();
        try {
            object= getUserInfo(vo.getEncryptedData(),sessionKey,vo.getOffset());
            if(null== object){
                return  DataResponse.failure(ErrorCodeUtil.E_407,"解密用户数据异常，请联系管理员！");
            }
        }catch (Exception e){
            e.printStackTrace();
            return  DataResponse.failure(ErrorCodeUtil.E_407,"解密用户数据异常，请联系管理员！");
        }
        try {
            String nickName = object.getString("nickName");
            String unionid = object.getString("unionId");
            String  gender = object.getString("gender");
            String  phoneNum = object.getString("phoneNumber");
            LoginRequestVO loginRequestVO = new LoginRequestVO();
            loginRequestVO.setType(vo.getType());
            loginRequestVO.setUsername(phoneNum);
            //调用登录的方法
            return  commonLogin(loginRequestVO);
        }catch ( Exception e){
            e.printStackTrace();
            return  DataResponse.failure(ErrorCodeUtil.E_500,"用户登录系统异常，原因为："+e.getMessage());
        }
    }


    /**
     * 获取信息
     */
    public  static com.alibaba.fastjson.JSONObject getUserInfo(String encryptedData, String sessionkey, String iv){
        // 被加密的数据
        byte[] dataByte =org.codehaus.xfire.util.Base64.decode(encryptedData);
        // 加密秘钥
        byte[] keyByte = org.codehaus.xfire.util.Base64.decode(sessionkey);
        // 偏移量
        byte[] ivByte = org.codehaus.xfire.util.Base64.decode(iv);
        try {
            // 如果密钥不足16位，那么就补足.  这个if 中的内容很重要
            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }
            // 初始化
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding","BC");
            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
            parameters.init(new IvParameterSpec(ivByte));
            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
            byte[] resultByte = cipher.doFinal(dataByte);
            if (null != resultByte && resultByte.length > 0) {
                String result = new String(resultByte, "UTF-8");
                return com.alibaba.fastjson.JSONObject.parseObject(result);
            }
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidParameterSpecException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        } catch (NoSuchProviderException e) {
            e.printStackTrace();
        }
        return null;
    }

}